home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / config / subst.c < prev    next >
C/C++ Source or Header  |  1993-01-29  |  12KB  |  592 lines

  1. /*  $Revision: 1.12 $
  2. **
  3. **  A C version of Henry Spencer's "subst" script.
  4. */
  5. #include <stdio.h>
  6. #include <signal.h>
  7. #include <errno.h>
  8.  
  9. #define LINESIZE        1024
  10. #define FNAMESIZE        1024
  11. #define PARAMSIZE        128
  12. #define WHITE(c)        ((c) == ' ' || (c) == '\t')
  13.  
  14.  
  15. /*
  16. **  AFS doesn't support hard links, so enable this #define.
  17. #define USE_RENAME
  18. */
  19.  
  20. /*
  21. **  If you don't have getopt in your C library, enable this #define.
  22. #define NEED_GETOPT
  23. */
  24.  
  25.  
  26. typedef struct _PAIR {
  27.     char    *Name;
  28.     int        Length;
  29.     char    *Value;
  30. } PAIR;
  31.  
  32. static char    *argv0;
  33. extern char    *optarg;
  34. extern int    optind;
  35.  
  36. extern void    exit();
  37. extern char    *malloc();
  38. extern char    *strcpy();
  39.  
  40.  
  41.  
  42. /*
  43. **  Local implementations of common C library functions.
  44. */
  45.  
  46. /*
  47. **  Return string represtation of errno.
  48. */
  49. static char *
  50. xstrerror()
  51. {
  52.     extern int    sys_nerr;
  53.     extern char    *sys_errlist[];
  54.     extern int    errno;
  55.     static char    buff[30];
  56.  
  57.     if (errno >= 0 && errno < sys_nerr)
  58.     return sys_errlist[errno];
  59.     (void)sprintf(buff, "Error code %d\n", errno);
  60.     return buff;
  61. }
  62.  
  63.  
  64. /*
  65. **  Return the first occurrence of 'c' in 'p' or NULL if not there.
  66. */
  67. static char *
  68. xstrchr(p, c)
  69.     register char    *p;
  70.     register char    c;
  71. {
  72.     for ( ; *p; p++)
  73.     if (*p == c)
  74.         return p;
  75.     return NULL;
  76. }
  77.  
  78.  
  79. /*
  80. **  Return the last occurrence of 'c' in 'p' or NULL if not there.
  81. */
  82. static char *
  83. xstrrchr(p, c)
  84.     register char    *p;
  85.     register char    c;
  86. {
  87.     register char    *ret;
  88.  
  89.     for (ret = NULL; *p; p++)
  90.     if (*p == c)
  91.         ret = p;
  92.     return ret;
  93. }
  94.  
  95.  
  96. /*
  97. **  Copy a string to malloc'd memory or exit.
  98. */
  99. static char *
  100. xstrdup(p)
  101.     char    *p;
  102. {
  103.     char    *new;
  104.  
  105.     if ((new = malloc(strlen(p) + 1)) == NULL) {
  106.     (void)fprintf(stderr, "%s: Can't copy \"%s\", %s\n",
  107.         argv0, p, xstrerror());
  108.     exit(1);
  109.     }
  110.     return strcpy(new, p);
  111. }
  112.  
  113. #if    defined(NEED_GETOPT)
  114.  
  115. #define TYPE    int
  116.  
  117. #define ERR(s, c)                    \
  118.     if (opterr) {                    \
  119.     char buff[2];                    \
  120.     buff[0] = c; buff[1] = '\n';            \
  121.     (void)write(2, av[0], (TYPE)strlen(av[0]));    \
  122.     (void)write(2, s, (TYPE)strlen(s));        \
  123.     (void)write(2, buff, 2);            \
  124.     }
  125.  
  126. int     opterr = 1;
  127. int     optind = 1;
  128. int     optopt;
  129. char    *optarg;
  130.  
  131. /*
  132. **  Return options and their values from the command line.
  133. **  This comes from the AT&T public-domain getopt published in mod.sources
  134. **  (i.e., comp.sources.unix before the great Usenet renaming).
  135. */
  136. int
  137. getopt(ac, av, opts)
  138.     int        ac;
  139.     char    *av[];
  140.     char    *opts;
  141. {
  142.     static int    i = 1;
  143.     char    *p;
  144.  
  145.     /* Move to next value from argv? */
  146.     if (i == 1) {
  147.     if (optind >= ac || av[optind][0] != '-' || av[optind][1] == '\0')
  148.         return EOF;
  149.     if (strcmp(av[optind], "--") == 0) {
  150.         optind++;
  151.         return EOF;
  152.     }
  153.     }
  154.  
  155.     /* Get next option character. */
  156.     if ((optopt = av[optind][i]) == ':' || (p = IDX(opts,  optopt)) == NULL) {
  157.     ERR(": illegal option -- ", optopt);
  158.     if (av[optind][++i] == '\0') {
  159.         optind++;
  160.         i = 1;
  161.     }
  162.     return '?';
  163.     }
  164.  
  165.     /* Snarf argument? */
  166.     if (*++p == ':') {
  167.     if (av[optind][i + 1] != '\0')
  168.         optarg = &av[optind++][i + 1];
  169.     else {
  170.         if (++optind >= ac) {
  171.         ERR(": option requires an argument -- ", optopt);
  172.         i = 1;
  173.         return '?';
  174.         }
  175.         optarg = av[optind++];
  176.     }
  177.     i = 1;
  178.     }
  179.     else {
  180.     if (av[optind][++i] == '\0') {
  181.         i = 1;
  182.         optind++;
  183.     }
  184.     optarg = NULL;
  185.     }
  186.  
  187.     return optopt;
  188. }
  189. #endif    /* defined(NEED_GETOPT) */
  190.  
  191.  
  192.  
  193. /*
  194. **  Simulate "mv $from $to" -- return no useful status.  We know that
  195. **  the $from and $to are on the same filesystem.
  196. */
  197. static void
  198. mv(from, to)
  199.     char    *from;
  200.     char    *to;
  201. {
  202.     if (unlink(to) < 0 && errno != ENOENT) {
  203.     (void)fprintf(stderr, "%s: Can't unlink %s, %s\n",
  204.         argv0, to, xstrerror());
  205.     return;
  206.     }
  207. #if    defined(USE_RENAME)
  208.     if (rename(from, to) < 0) {
  209.     (void)fprintf(stderr, "%s: Can't rename %s to %s, %s\n",
  210.         argv0, from, to, xstrerror());
  211.     return;
  212.     }
  213. #else
  214.     if (link(from, to) < 0) {
  215.     (void)fprintf(stderr, "%s: Can't link %s to %s, %s\n",
  216.         argv0, from, to, xstrerror());
  217.     return;
  218.     }
  219.     if (unlink(from) < 0)
  220.     (void)fprintf(stderr, "%s: Can't unlink %s, %s\n",
  221.         argv0, from, xstrerror());
  222. #endif    /* defined(USE_RENAME) */
  223. }
  224.  
  225.  
  226. /*
  227. **  Simulate "cmp -s $n1 $n2" -- return 0 if files are the same.
  228. */
  229. static int
  230. cmp(n1, n2)
  231.     char    *n1;
  232.     char    *n2;
  233. {
  234.     FILE    *f1;
  235.     FILE    *f2;
  236.     int        c;
  237.  
  238.     if ((f1 = fopen(n1, "r")) == NULL)
  239.     return 1;
  240.     if ((f2 = fopen(n2, "r")) == NULL) {
  241.     (void)fclose(f1);
  242.     return 1;
  243.     }
  244.     while ((c = getc(f1)) != EOF)
  245.     if (getc(f2) != c) {
  246.         (void)fclose(f1);
  247.         (void)fclose(f2);
  248.         return 1;
  249.     }
  250.     if (getc(f2) != EOF) {
  251.     (void)fclose(f1);
  252.     (void)fclose(f2);
  253.     return 1;
  254.     }
  255.     (void)fclose(f1);
  256.     (void)fclose(f2);
  257.     return 0;
  258. }
  259.  
  260.  
  261. /*
  262. **  If line does not look like a template, return NULL, otherwise modify
  263. **  it to delete the trailing gunk and return the start of the template.
  264. */
  265. static char *
  266. istemplate(line)
  267.     char    *line;
  268. {
  269.     char    *p;
  270.     char    *start;
  271.  
  272.     /* Find "=()<" and remember where it starts. */
  273.     for (p = line; (p = xstrchr(p, '=')) != NULL; p++)
  274.     if (p[1] == '(' && p[2] == ')' && p[3] == '<')
  275.         break;
  276.     if (p == NULL)
  277.     return NULL;
  278.     start = &p[4];
  279.  
  280.     /* Now find ">()=" and nip it off. */
  281.     for (p = start; (p = xstrchr(p, '>')) != NULL; p++)
  282.     if (p[1] == '(' && p[2] == ')' && p[3] == '=') {
  283.         *p++ = '\n';
  284.         *p = '\0';
  285.         return start;
  286.     }
  287.     return NULL;
  288. }
  289.  
  290.  
  291. /*
  292. **  Splice three strings together, returning an allocated copy.
  293. */
  294. static char *
  295. splice(s1, s2, s3)
  296.     char    *s1;
  297.     char    *s2;
  298.     char    *s3;
  299. {
  300.     int        i;
  301.     char    *new;
  302.  
  303.     i = strlen(s1) + strlen(s2) + strlen(s3) + 1;
  304.     if ((new = malloc(i)) == NULL) {
  305.     (void)fprintf(stderr, "%s: Can't splice %s+%s+%s, %s\n",
  306.         argv0, s1, s2, s3, xstrerror());
  307.     exit(1);
  308.     }
  309.     (void)sprintf(new, "%s%s%s", s1, s2, s3);
  310.     return new;
  311. }
  312.  
  313.  
  314. /*
  315. **  Substitute all found patterns in the line and print it.  Using the goto
  316. **  makes the code more clear than using do/while.
  317. */
  318. static int
  319. doline(f, out, line, tp, end)
  320.     char    *f;
  321.     FILE    *out;
  322.     char    *line;
  323.     PAIR    *tp;
  324.     PAIR    *end;
  325. {
  326.     char    *p;
  327.     char    *new;
  328.     char    save;
  329.     int        count;
  330.  
  331.     for (count = 0, line = xstrdup(line); tp < end; tp++) {
  332. Again:
  333.     for (p = line; (p = xstrchr(p, tp->Name[0])) != NULL; p++)
  334.         if (strncmp(p, tp->Name, tp->Length) == 0) {
  335.         save = *p;
  336.         *p = '\0';
  337.         count++;
  338.         new = splice(line, tp->Value, p + tp->Length);
  339.         *p = save;
  340.         if (strcmp(new, line) == 0) {
  341.             (void)fprintf(stderr, "%s:  subst loop in %s:\n\t%s\n",
  342.                 argv0, f, line);
  343.             free(new);
  344.             break;
  345.         }
  346.         free(line);
  347.         line = new;
  348.         goto Again;
  349.         }
  350.     }
  351.     if (count > 0 && fputs(line, out) == EOF) {
  352.     (void)fprintf(stderr, "%s:  can't write %s, %s\n",
  353.         argv0, f, xstrerror());
  354.     free(line);
  355.     return -1;
  356.     }
  357.     free(line);
  358.     return count;
  359. }
  360.  
  361.  
  362. /*
  363. **  Process one file, carefully substituting it in place.
  364. */
  365. static void
  366. Process(f, Table, end)
  367.     char    *f;
  368.     PAIR    *Table;
  369.     PAIR    *end;
  370. {
  371.     char    new[FNAMESIZE];
  372.     char    old[FNAMESIZE];
  373.     char    line[LINESIZE];
  374.     int        bad;
  375.     int        i;
  376.     int        count;
  377.     FILE    *in;
  378.     FILE    *out;
  379.     FILE    *temp;
  380.     char    *p;
  381.  
  382.     /* First, figure out temporary names. */
  383.     if ((p = xstrrchr(f, '/')) == NULL) {
  384.     (void)strcpy(new, "substtmp.new");
  385.     (void)strcpy(old, "substtmp.old");
  386.     }
  387.     else {
  388.     *p = '\0';
  389.     (void)sprintf(new, "%s/substtmp.new", f);
  390.     (void)sprintf(old, "%s/substtmp.old", f);
  391.     *p = '/';
  392.     }
  393.  
  394.     /* Test existences. */
  395.     if ((in = fopen(f, "r")) == NULL) {
  396.     (void)fprintf(stderr, "%s: can't open %s, %s\n",
  397.         argv0, f, xstrerror());
  398.     return;
  399.     }
  400.     if ((temp = fopen(new, "r")) != NULL) {
  401.     (void)fclose(in);
  402.     (void)fprintf(stderr, "%s: %s exists, cannot proceed\n",
  403.         argv0, new);
  404.     exit(1);
  405.     }
  406.     if ((temp = fopen(old, "r")) != NULL) {
  407.     (void)fprintf(stderr, "%s: %s exists, cannot proceed\n",
  408.         argv0, old);
  409.     exit(1);
  410.     }
  411.     temp = fopen(old, "w");
  412.     out = fopen(new, "w");
  413.     if (out == NULL || temp == NULL) {
  414.     if (temp != NULL)
  415.         (void)fclose(temp);
  416.     (void)unlink(old);
  417.     if (out != NULL)
  418.         (void)fclose(out);
  419.     (void)unlink(new);
  420.     (void)fprintf(stderr, "%s: cannot create temporaries %s and %s\n",
  421.         argv0, old, new);
  422.     exit(1);
  423.     }
  424.     (void)fclose(temp);
  425.  
  426.     /* Generate the new version. */
  427.     for (i = 1, bad = 0; fgets(line, sizeof line, in) != NULL; i++) {
  428.     if ((p = xstrchr(line, '\n')) == NULL) {
  429.         (void)fprintf(stderr,
  430.           "%s: Line %d of %s is too long (or doesn't end with a newline)\n",
  431.             argv0, i, f);
  432.         bad++;
  433.         break;
  434.     }
  435.     (void)fputs(line, out);
  436.     if ((p = istemplate(line)) != NULL) {
  437.         if ((count = doline(f, out, p, Table, end)) < 0) {
  438.         bad++;
  439.         break;
  440.         }
  441.         if (count > 0) {
  442.         (void)fgets(line, sizeof line, in);
  443.         i++;
  444.         }
  445.         else
  446.         (void)fprintf(stderr,
  447.             "%s: %s:%d unknown parameter or bad line:\n\t%s",
  448.             argv0, f, i, p);
  449.     }
  450.     }
  451.     (void)fclose(in);
  452.     if (fflush(out) == EOF || fclose(out) == EOF) {
  453.     (void)fprintf(stderr, "%s: can't close %s, %s\n",
  454.         argv0, f, xstrerror());
  455.     bad++;
  456.     }
  457.  
  458.     if (bad || cmp(new, f) == 0) {
  459.     (void)unlink(old);
  460.     (void)unlink(new);
  461.     (void)printf("%s: unchanged\n", f);
  462.     return;
  463.     }
  464.  
  465.     /* Substitute new for old the only safe way -- ignore signals. */
  466.     (void)signal(SIGHUP, SIG_IGN);
  467.     (void)signal(SIGINT, SIG_IGN);
  468.     (void)signal(SIGTERM, SIG_IGN);
  469.     mv(f, old);
  470.     mv(new, f);
  471.     (void)signal(SIGHUP, SIG_DFL);
  472.     (void)signal(SIGINT, SIG_DFL);
  473.     (void)signal(SIGTERM, SIG_DFL);
  474.     (void)printf("%s: updated\n", f);
  475.     (void)unlink(old);
  476. }
  477.  
  478.  
  479. /*
  480. **  Print usage message and exit.
  481. */
  482. static void
  483. Usage()
  484. {
  485.     (void)fprintf(stderr, "Usage: %s -f file victims...\n", argv0);
  486.     exit(1);
  487. }
  488.  
  489.  
  490. int
  491. main(ac, av)
  492.     int        ac;
  493.     char    *av[];
  494. {
  495.     static char    NIL[] = "";
  496.     char    *ctlfile;
  497.     char    *p;
  498.     char    *dest;
  499.     FILE    *F;
  500.     int        i;
  501.     char    buff[LINESIZE];
  502.     char    name[PARAMSIZE];
  503.     PAIR    *Table;
  504.     PAIR    *tp;
  505.  
  506.     /* Set defaults. */
  507.     ctlfile = NULL;
  508.     argv0 = av[0];
  509.  
  510.     /* Parse JCL. */
  511.     while ((i = getopt(ac, av, "f:")) != EOF)
  512.     switch (i) {
  513.     default:
  514.         Usage();
  515.         /* NOTREACHED */
  516.     case 'f':
  517.         if (ctlfile != NULL)
  518.         Usage();
  519.         ctlfile = optarg;
  520.         break;
  521.     }
  522.     ac -= optind;
  523.     av += optind;
  524.  
  525.     /* Open control file, count lines, allocate table. */
  526.     if ((F = fopen(ctlfile, "r")) == NULL) {
  527.     (void)fprintf(stderr, "%s: Can't open %s to read it, %s\n",
  528.         argv0, ctlfile, xstrerror());
  529.     exit(1);
  530.     }
  531.     for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++)
  532.     continue;
  533.     if ((Table = (PAIR *)malloc(i * sizeof *Table)) == NULL) {
  534.     (void)fprintf(stderr, "%s: Can't allocate %d table elements, %s\n",
  535.         argv0, i, xstrerror());
  536.     exit(1);
  537.     }
  538.  
  539.     /* Now parse the table. */
  540.     (void)fseek(F, 0L, 0);
  541.     for (i = 1, tp = Table; fgets(buff, sizeof buff, F) != NULL; i++) {
  542.     if ((p = xstrchr(buff, '\n')) == NULL) {
  543.         (void)fprintf(stderr, "%s: Line %d of %s is too long\n",
  544.             argv0, i, ctlfile);
  545.         exit(1);
  546.     }
  547.     *p = '\0';
  548.  
  549.     /* Skip empty lines, comment lines, and all-blank lines. */
  550.     if (buff[0] == '\0' || buff[0] == '#')
  551.         continue;
  552.     for (p = buff; WHITE(*p); p++)
  553.         continue;
  554.     if (*p == '\0')
  555.         continue;
  556.  
  557.     /* Find end of first word, copy second word (or empty string) */
  558.     for (p = buff; *p && !WHITE(*p); p++)
  559.         continue;
  560.     if (*p == '\0')
  561.         tp->Value = NIL;
  562.     else {
  563.         for (*p++ = '\0'; *p && WHITE(*p); p++)
  564.         continue;
  565.         tp->Value = xstrdup(p);
  566.  
  567.         /* Turn things like \& into &. */
  568.         for (p = dest = tp->Value; *p; p++)
  569.         *dest++ = (*p == '\\' && p[1] != '\0') ? *++p : *p;
  570.         *dest = '\0';
  571.     }
  572.  
  573.     /* Turn first word into something directly searchable. */
  574.     if (strlen(buff) > sizeof name - 4) {
  575.         (void)fprintf(stderr, "%s: Parameter %s is too long\n",
  576.             argv0, buff);
  577.         exit(1);
  578.     }
  579.     (void)sprintf(name, "@<%s>@", buff);
  580.     tp->Name = xstrdup(name);
  581.     tp->Length = strlen(tp->Name);
  582.     tp++;
  583.     }
  584.     (void)fclose(F);
  585.  
  586.     while (*av != NULL)
  587.     Process(*av++, Table, tp);
  588.  
  589.     exit(0);
  590.     /* NOTREACHED */
  591. }
  592.